// $Id: ptr.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2008-2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_PTR_HPP
#define XPCOM_PTR_HPP

#include <xpcom/config.hpp>
#ifndef XPCOM_NO_IOSTREAM
#include <iosfwd>
#endif // XPCOM_NO_IOSTREAM
#include <boost/detail/workaround.hpp> // XXX for operator bool codes
#include <xpcom/assert.hpp>
#include <xpcom/exception.hpp>
#include <xpcom/interface.hpp>
#include <xpcom/type_info.hpp>
#include <xpcom/proxy/interface_proxy.hpp>
#include <xpcom/detail/sp_convertible.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace xpcom {

namespace detail {

template <typename Ptr>
struct ptr_access;

} // namespace xpcom::detail

template <typename T> class ref_ptr;
template <typename T> class noref_ptr;

template <typename Fn>
class ptr_factory;

// Base class of XPCOM smart pointers
template <typename T>
class ptr_base
{
    typedef ptr_base<T> this_type;
protected:
    ptr_base() :
        p_(0)
    {
    }

    ptr_base(const ptr_base& rhs) :
        p_(rhs.p_)
    {
    }

    template <typename U>
    ptr_base(const ptr_base<U>& rhs) :
        p_(rhs.get())
    {
    }

    explicit
    ptr_base(T* p) :
        p_(p)
    {
    }

public:
    // Pointer access
    proxy::interface_proxy<T>* operator->() const
    {
        XPCOM_ASSERT_PTR(p_);
#ifdef XPCOM_USE_INHERITED_INTERFACE_PROXY
        return static_cast<proxy::interface_proxy<T>* >(p_);
#else
        return reinterpret_cast<proxy::interface_proxy<T>* >(p_);
#endif
    }

    T* get() const
    {
        return p_;
    }

    // Implicit conversion to "bool"
    // Following code is taken from boost/shared_ptr.hpp
#if (defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, < 0x570)) || defined(__CINT__)
    operator bool() const
    {
        return p_ != 0;
    }

#elif defined(_MANAGED)
    static void unspecified_bool(this_type***)
    {
    }

    typedef void (*unspecified_bool_type)(this_type***);

    operator unspecified_bool_type() const
    {
        return p_ == 0 ? 0 : unspecified_bool;
    }

#elif \
    (defined(__MWERKS__) && BOOST_WORKAROUND(__MWERKS__, < 0x3200)) || \
    (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 304)) || \
    (defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, <= 0x590))

    typedef T* (this_type::*unspecified_bool_type)() const;

    operator unspecified_bool_type() const
    {
        return p_ == 0 ? 0 : &this_type::get;
    }

#else
    typedef T* this_type::*unspecified_bool_type;

    operator unspecified_bool_type() const
    {
        return p_ == 0 ? 0 : &this_type::p_;
    }

#endif

    // operator! is redundant, but some compilers need it
    bool operator!() const
    {
        return p_ == 0;
    }

    // Compare two pointer for same instance
    template <typename U>
    bool equal(const ptr_base<U>& rhs) const
    {
        return interface_instance_equal(
                static_cast<interface*>(p_),
                static_cast<interface*>(rhs.get()));
    }

protected:
    void swap(ptr_base& rhs)
    {
        T* tmp = p_;
        p_ = rhs.p_;
        rhs.p_ = tmp;
    }

    T* p_;
};

//
// XPCOM smart pointer class.
//
// This class is used mainly for managing XPCOM interface pointers.
// It can also be used to manage references of object that implements XPCOM
// interfaces.
//
template <typename T>
class ref_ptr : public ptr_base<T>
{
    typedef ref_ptr<T> this_type;
    typedef ptr_base<T> base_type;
public:
    typedef T element_type;

    ref_ptr() :
        base_type()
    {
    }

    ref_ptr(const ref_ptr& rhs) :
        base_type(rhs)
    {
        if (p_)
            p_->add_ref();
    }

    template <typename U>
#ifndef XPCOM_NO_SP_CONVERTIBLE
    ref_ptr(const ref_ptr<U>& rhs,
            typename detail::sp_enable_if_convertible<U, T>::type = detail::sp_empty()) :
#else
    ref_ptr(const ref_ptr<U>& rhs) :
#endif
        base_type(rhs)
    {
        if (p_)
            p_->add_ref();
    }

    template <typename U>
#ifndef XPCOM_NO_SP_CONVERTIBLE
    ref_ptr(const noref_ptr<U>& rhs,
            typename detail::sp_enable_if_convertible<U, T>::type = detail::sp_empty()) :
#else
    ref_ptr(const noref_ptr<U>& rhs) :
#endif
        base_type(rhs)
    {
        if (p_)
            p_->add_ref();
    }

    explicit
    ref_ptr(T* p, bool add_ref = true) :
        base_type(p)
    {
        if (p_ && add_ref)
            p_->add_ref();
    }

    template <typename Fn>
    ref_ptr(const ptr_factory<Fn>& rhs) :
        base_type()
    {
        rhs(p_);
    }

    ~ref_ptr()
    {
        if (p_)
            p_->release();
    }

    ref_ptr& operator=(const ref_ptr& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    template <typename U>
    ref_ptr& operator=(const ref_ptr<U>& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    template <typename U>
    ref_ptr& operator=(const noref_ptr<U>& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    template <typename Fn>
    ref_ptr& operator=(const ptr_factory<Fn>& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    void reset()
    {
        if (p_)
            this_type().swap(*this);
    }

    void reset(T* p, bool add_ref = true)
    {
        XPCOM_ASSERT(!p || p != p_); // catch self-reset errors
        this_type(p, add_ref).swap(*this);
    }

    T* release()
    {
        T* tmp = p_;
        p_ = 0;
        return tmp;
    }

    void swap(ref_ptr& rhs)
    {
        base_type::swap(rhs);
    }

private:
    using base_type::p_;

    friend struct detail::ptr_access<this_type>;
};

//
// XPCOM smart pointer class which does not own reference.
//
// This class is used mainly as parameter of functions.
// Please use it with caution.
//
template <typename T>
class noref_ptr : public ptr_base<T>
{
    typedef noref_ptr<T> this_type;
    typedef ptr_base<T> base_type;
public:
    typedef T element_type;

    noref_ptr() :
        base_type()
    {
    }

    noref_ptr(const noref_ptr& rhs) :
        base_type(rhs)
    {
    }

    template <typename U>
#ifndef XPCOM_NO_SP_CONVERTIBLE
    noref_ptr(const noref_ptr<U>& rhs,
            typename detail::sp_enable_if_convertible<U, T>::type = detail::sp_empty()) :
#else
    noref_ptr(const noref_ptr<U>& rhs) :
#endif
        base_type(rhs)
    {
    }

    template <typename U>
#ifndef XPCOM_NO_SP_CONVERTIBLE
    noref_ptr(const ref_ptr<U>& rhs,
            typename detail::sp_enable_if_convertible<U, T>::type = detail::sp_empty()) :
#else
    noref_ptr(const ref_ptr<U>& rhs) :
#endif
        base_type(rhs)
    {
    }

    noref_ptr(T* p) :
        base_type(p)
    {
    }

    noref_ptr& operator=(const noref_ptr& rhs)
    {
        p_ = rhs.p_;
        return *this;
    }

    template <typename U>
    noref_ptr& operator=(const noref_ptr<U>& rhs)
    {
        p_ = rhs.get();
        return *this;
    }

    template <typename U>
    noref_ptr& operator=(const ref_ptr<U>& rhs)
    {
        p_ = rhs.get();
        return *this;
    }

    noref_ptr& operator=(T* p)
    {
        p_ = p;
        return *this;
    }

    void reset()
    {
        p_ = 0;
    }

    void reset(T* p)
    {
        p_ = p;
    }

    void swap(noref_ptr& rhs)
    {
        base_type::swap(rhs);
    }

private:
    using base_type::p_;

    friend struct detail::ptr_access<this_type>;
};

// operators
template <typename T, typename U>
inline bool operator==(const ptr_base<T>& lhs, const ptr_base<U>& rhs)
{
    return lhs.get() == rhs.get();
}

template <typename T, typename U>
inline bool operator!=(const ptr_base<T>& lhs, const ptr_base<U>& rhs)
{
    return lhs.get() != rhs.get();
}

#if __GNUC__ == 2 && __GNUC_MINOR__ <= 96
// Resolve the ambiguity between our op!= and the one in rel_ops
template <typename T>
inline bool operator!=(const ptr_base<T>& lhs, const ptr_base<T>& rhs)
{
    return lhs.get() != rhs.get();
}
#endif

template <typename T>
inline bool operator<(const ref_ptr<T>& lhs, const ref_ptr<T>& rhs)
{
    return lhs.get() < rhs.get();
}

template <typename T>
inline bool operator<(const noref_ptr<T>& lhs, const noref_ptr<T>& rhs)
{
    return lhs.get() < rhs.get();
}

// swap
template <typename T>
inline void swap(ref_ptr<T>& lhs, ref_ptr<T>& rhs)
{
    lhs.swap(rhs);
}

template <typename T>
inline void swap(noref_ptr<T>& lhs, noref_ptr<T>& rhs)
{
    lhs.swap(rhs);
}

// boost::mem_fn support
template <typename T>
inline T* get_pointer(const ptr_base<T>& p)
{
    return p.get();
}

//
// Factory class for creating objects that returning XPCOM pointer.
//
// Its main purpose is to transform query_interface like function into a more
// convenience form, you can write something like:
//
//  ref_ptr<IFoo> = some_function(some_class_id);
//
// without give the interface uuid, and receives the value  to a ref_ptr
// directly.
//
// The only function which ptr_factory template class must have is:
//
//  template <typename T> void operator()(T*& p) const;
//
// This function writes a pointer of type T to p, which holds a strong
// reference. Exceptions can be thrown from the function to indicate error
// status.
//
// By default, the ptr_factory implementation takes a function object f, and
// call the function:
//
//  result_t f(const uuid& iid, void** obj);
//
// boost::bind can be used to make this functor for interface methods.
// And any specialization of the ptr_factory template can be made for proper
// needs.
//
template <typename Fn>
class ptr_factory
{
public:
    ptr_factory(const Fn& f) : f_(f)
    {
    }

    template <typename T>
    void operator()(T*& p) const
    {
        // result_t operator()(const uuid& iid, void** obj);
        f_(typeid_of<T>(), reinterpret_cast<void**>(&p)) | throw_exception;
    }

private:
    Fn f_;
};

//
// Convenience function for making ptr_factory based on argument type.
//
template <typename Fn>
ptr_factory<Fn> make_ptr_factory(const Fn& f)
{
    return ptr_factory<Fn>(f);
}

//
// Casts, static_pointer_cast, dynamic_pointer_cast, polymorphic_pointer_cast
//
//  ref_ptr<Foo> p;
//  ref_ptr<Bar> p1 = static_pointer_cast<Bar>(p);
//  ref_ptr<Bar> p2 = dynamic_pointer_cast<Bar>(p);
//  ref_ptr<Bar> p3 = polymorphic_pointer_cast<Bar>(p); // throws if failed
//
// Note: use static_pointer_cast for downcasting with caution, it might be dangerous
//
template <typename T, typename U>
ref_ptr<T> static_pointer_cast(const ref_ptr<U>& p)
{
    return ref_ptr<T>(static_cast<T*>(p.get()));
}

template <typename T, typename U>
noref_ptr<T> static_pointer_cast(const noref_ptr<U>& p)
{
    return static_cast<T*>(p.get());
}

template <typename T, typename U>
ref_ptr<T> dynamic_pointer_cast(const ptr_base<U>& p)
{
    T* pt = 0;
    if (p) {
        // XXX qi implementation should leave pt at 0 on failure
        p.get()->query_interface(
                typeid_of(pt), reinterpret_cast<void**>(&pt));
    }
    return ref_ptr<T>(pt, false);
}

template <typename T, typename U>
ref_ptr<T> polymorphic_pointer_cast(const ptr_base<U>& p)
{
    T* pt = 0;
    if (p) {
        p.get()->query_interface(
                typeid_of(pt), reinterpret_cast<void**>(&pt)) | throw_exception;
        XPCOM_ASSERT_PTR(pt);
    }
    return ref_ptr<T>(pt, false);
}

// IO
#ifndef XPCOM_NO_IOSTREAM

template <typename CharT, typename Traits, typename T>
std::basic_ostream<CharT, Traits>& operator<<(
        std::basic_ostream<CharT, Traits>& os,
        const ptr_base<T>& rhs)
{
    os << rhs.get();
    return os;
}

#endif // XPCOM_NO_IOSTREAM

#ifndef XPCOM_DISABLE_TYPEDEFS
typedef ref_ptr<interface> interface_ptr;
typedef noref_ptr<interface> interface_nptr;
#endif // XPCOM_DISABLE_TYPEDEFS

} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_PTR_HPP
